﻿using System.Collections.Generic;
using System.Linq;
using Expect.Core.Errors;
using Expect.Core.Interfaces;
using System;

namespace Expect.Core.Helpers
{    
    /// <summary>
    /// Helpers for fixture and specification resolution
    /// </summary>
    public static class ResolutionHelpers
    {
        /// <summary>
        /// Run a method for a fixture row
        /// </summary>
        /// <param name="rw"></param>
        /// <param name="methodName"></param>
        /// <param name="target"></param>
        /// <param name="nbArgs"></param>
        /// <param name="act"></param>
        /// <returns></returns>
        public static bool? RunRowMethod(FixtureRow rw, string methodName, string target, int nbArgs, Func<bool?> act)
        {
            try
            {
                var res = act();
                if (res.HasValue)
                {
                    for (int i = 0; i < rw.Count; i++)
                    {
                        rw[i].Status.Success = res;
                    }
                }

                return res;
            }
            catch (MethodNotFoundException)
            {
                rw.Status.AddError(ElementNotFoundError.Method(methodName, target, nbArgs));
                rw.Status.Success = null;
            }

            return false;
        }

        /// <summary>
        /// Return the specification from a setup or teardown method
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="container"></param>
        /// <param name="spec"></param>
        /// <returns></returns>
        public static SpecificationDefinition ApplySpecificationFor<T>(this ISpecificationContainer container, SpecificationDefinition spec) where T : Attribute
        {
            var method =
                container.GetType().GetMethods().FirstOrDefault(t => t.GetCustomAttributes(typeof(T), true).Length == 1 );

            if (method == null)
                throw new InvalidOperationException("Container doesn't contain a method marked with " + typeof(T).Name);

            return (SpecificationDefinition)method.Invoke(container, new object[] {spec});
        }
    }
}
